#include "StdAfx.h"
#include "ArcAnnulus.h"


CArcAnnulus::CArcAnnulus(double x, double y, double inner, double outer,
  double start, double end)
{
  arc_annulus_.x       = x;
  arc_annulus_.y       = y;
  arc_annulus_.inner   = inner;
  arc_annulus_.outer   = outer;
  arc_annulus_.start   = start;
  arc_annulus_.end     = end;

  hit_code_ = kHitNothing;

  AdjustHandles();
}


CArcAnnulus::~CArcAnnulus(void)
{
}

void CArcAnnulus::LeftButtonDown_slot(CWnd* sender, const MouseArgument& arg)
{
  if (!visible_) {
    return;
  }

  if (CursorOn(arg.point)) {
    HitTest(arg.point);

    tracking_	= true;
    anchor_	= arg.point;
    sender->SetCapture();

    if (!selected_) {
      sender->Invalidate(FALSE);
    }

    selected_ = true;
  } else {
    if(selected_) {
      sender->Invalidate(FALSE);
      selected_ = false;
    }
  }
}

void CArcAnnulus::LeftButtonUp_slot(CWnd* sender, const MouseArgument& arg)
{
  if(tracking_) {
    tracking_	= false;
    AdjustHandles();

    if(sender->GetCapture() == sender) {
      ::ReleaseCapture();
    }

    sender->Invalidate(FALSE);
  }
}

void CArcAnnulus::MouseMove_slot(CWnd* sender, const MouseArgument& arg)
{
  if(tracking_) {
    TrackTo(arg.point);
    AdjustHandles();

    sender->Invalidate(FALSE);
  }
}

bool CArcAnnulus::CursorSet_slot(CWnd* sender, const MouseArgument& arg)
{
  if (!visible_) {
    return false;
  }

  if(arg.flags!=HTCLIENT) {
    return false;
  }

  HitTest(arg.point);

  geometry::Point2D center(arc_annulus_.x, arc_annulus_.y);
  bool normal = arc_annulus_.outer>=arc_annulus_.inner;

  switch(hit_code_)
  {
  case kHitInnerBorder:
  case kHitOuterBorder:
    ::SetCursor(kBorderCursors[Quadrant(center, arg.point)]);
    return true;

  case kHitStartSpoke:
    ::SetCursor(kBorderCursors[(Quadrant(arc_annulus_.start)+2)%8]);
    return true;

  case kHitEndSpoke:
    ::SetCursor(kBorderCursors[(Quadrant(arc_annulus_.end)+2)%8]);
    return true;

  case kHitInnerStart:
    ::SetCursor(kBorderCursors[(Quadrant(arc_annulus_.start)+normal?1:3)%8]);
    return true;

  case kHitOuterStart:
    ::SetCursor(kBorderCursors[(Quadrant(arc_annulus_.start)+normal?3:1)%8]);
    return true;

  case kHitInnerEnd:
    ::SetCursor(kBorderCursors[(Quadrant(arc_annulus_.end)+normal?3:1)%8]);
    return true;

  case kHitOuterEnd:
    ::SetCursor(kBorderCursors[(Quadrant(arc_annulus_.end)+normal?1:3)%8]);
    return true;

  case kHitMiddle:
    ::SetCursor(kSizeAll);
    return true;

  default : return false;
  }
}

void CArcAnnulus::Draw(Hlong halcon_wnd_handle)
{
  if (!visible_) {
    return;
  }

  Halcon::set_draw(halcon_wnd_handle, "margin");
  Halcon::set_line_width(halcon_wnd_handle, kBorderWidth);

  if (selected_) {
    Halcon::set_rgb(halcon_wnd_handle, GetRValue(handle_color_),
      GetGValue(handle_color_), GetBValue(handle_color_));

    for (int i=0; i<4; ++i) {
      Halcon::disp_circle(halcon_wnd_handle, handles_[i].y(), handles_[i].x(),
        handles_[i].width()/2);
    }
  }

  Halcon::set_rgb(halcon_wnd_handle, GetRValue(border_color_),
    GetGValue(border_color_), GetBValue(border_color_));

  Halcon::disp_arc(halcon_wnd_handle, arc_annulus_.y, arc_annulus_.x,
    RADIAN(sweep_), vertexes_[kInnerEnd].GetY<long>(),
    vertexes_[kInnerEnd].GetX<long>());
  Halcon::disp_arc(halcon_wnd_handle, arc_annulus_.y, arc_annulus_.x,
    RADIAN(sweep_), vertexes_[kOuterEnd].GetY<long>(),
    vertexes_[kOuterEnd].GetX<long>());

  Halcon::disp_line(halcon_wnd_handle, vertexes_[kInnerStart].y(),
    vertexes_[kInnerStart].x(), vertexes_[kOuterStart].y(),
    vertexes_[kOuterStart].x());
  Halcon::disp_line(halcon_wnd_handle, vertexes_[kInnerEnd].y(),
    vertexes_[kInnerEnd].x(), vertexes_[kOuterEnd].y(),
    vertexes_[kOuterEnd].x());

  Halcon::disp_cross(halcon_wnd_handle, arc_annulus_.y, arc_annulus_.x,
    kMarkerSize, 0);

  geometry::Point2D point_start = 0.3*vertexes_[kInnerStart] +
    0.7*vertexes_[kOuterStart];
  geometry::Point2D point_end   = 0.3*vertexes_[kInnerEnd] +
    0.7*vertexes_[kOuterEnd];

  Halcon::disp_arrow(halcon_wnd_handle, vertexes_[kInnerStart].y(),
    vertexes_[kInnerStart].x(), point_start.y(), point_start.x(), kArrowHeight*0.75);
  Halcon::disp_arrow(halcon_wnd_handle, vertexes_[kInnerEnd].y(),
    vertexes_[kInnerEnd].x(), point_end.y(), point_end.x(), kArrowHeight*0.75);
}

void CArcAnnulus::AdjustHandles(void)
{
  geometry::Point2D center(arc_annulus_.x, arc_annulus_.y);

	vertexes_[kInnerStart]	= Polar2Cartesian2(center, arc_annulus_.inner,
    arc_annulus_.start);
	vertexes_[kOuterStart]	= Polar2Cartesian2(center, arc_annulus_.outer,
    arc_annulus_.start);
	vertexes_[kInnerEnd]	  = Polar2Cartesian2(center, arc_annulus_.inner,
    arc_annulus_.end);
	vertexes_[kOuterEnd]	  = Polar2Cartesian2(center, arc_annulus_.outer,
    arc_annulus_.end);

	//*******************InnerStart*********************
	handles_[kInnerStart]	= geometry::Rect2D(vertexes_[kInnerStart], kHandleSize);

	//********************OuterStart********************
	handles_[kOuterStart] = geometry::Rect2D(vertexes_[kOuterStart], kHandleSize);

	//*******************InnerEnd*********************
	handles_[kInnerEnd]	= geometry::Rect2D(vertexes_[kInnerEnd], kHandleSize);

	//********************OuterEnd********************
	handles_[kOuterEnd] = geometry::Rect2D(vertexes_[kOuterEnd], kHandleSize);

	//****************BoundingBox****************
	bounding_box_	= geometry::Rect2D(arc_annulus_.x, arc_annulus_.y,
    arc_annulus_.outer);

	//****************SweepAngle*****************
	sweep_ = AngleSweep(arc_annulus_.start, arc_annulus_.end);
}

bool CArcAnnulus::CursorOn(const geometry::Point2D &pt) const
{
  if(selected_) {
    for(int i=0; i<4; ++i) {
      if(handles_[i].PtInRect(pt)) {
        return true;
      }
    }
  }

  double inner = min(arc_annulus_.inner, arc_annulus_.outer);
  double outer = max(arc_annulus_.inner, arc_annulus_.outer);

  geometry::Point2D cpt(arc_annulus_.x, arc_annulus_.y);

  bool bInsideCenter	= PtInCircle(pt, cpt, min(inner, kMarkerSize) + kTolerance);
  if(bInsideCenter) {
    return true;
  }

  bool bInsideOuter	 = PtInCircle(pt, cpt, outer + kTolerance);
  bool bOutsideInner = !PtInCircle(pt, cpt, max(0, inner - kTolerance));

  // Vector from circle arc center to cursor hot point
  geometry::Point2D vec(pt.x() - arc_annulus_.x,  arc_annulus_.y - pt.y());
  double angle = VectorAngle(vec.x(), vec.y());
  bool bAngleIn = AngleInRange(angle, arc_annulus_.start, arc_annulus_.end);
  if(bInsideOuter && bOutsideInner && bAngleIn) {
    return true;
  }

  double width = outer-inner;
  geometry::Point2D vertexes[4] = {vertexes_[kInnerStart],
    vertexes_[kInnerStart], vertexes_[kOuterStart], vertexes_[kOuterStart] };
  InflateRect(vertexes, width, 0, 0, kTolerance);
  bool bInsideStart = PtInRect(vertexes, pt);
  if(bInsideStart) {
    return true;
  }

  vertexes[0] = vertexes_[kOuterEnd];
  vertexes[1] = vertexes_[kOuterEnd];
  vertexes[2] = vertexes_[kInnerEnd];
  vertexes[3] = vertexes_[kInnerEnd];
  InflateRect(vertexes, width, 0, 0, kTolerance);
  BOOL bInsideEnd = PtInRect(vertexes, pt);
  if(bInsideEnd) {
    return true;
  }

  return false;
}

void CArcAnnulus::HitTest(const geometry::Point2D& pt)
{
  hit_code_ = kHitNothing;

  // Test if mouse hit annulus outer start handle
  if(handles_[kOuterStart].PtInRect(pt))
  {
    hit_code_ = kHitOuterStart; return;
  }

  // Test if mouse hit annulus outer end handle
  if(handles_[kOuterEnd].PtInRect(pt))
  {
    hit_code_ = kHitOuterEnd; return;
  }

  // Test if mouse hit annulus inner start handle
  if(handles_[kInnerStart].PtInRect(pt))
  {
    hit_code_ = kHitInnerStart; return;
  }

  // Test if mouse hit annulus inner end handle
  if(handles_[kInnerEnd].PtInRect(pt))
  {
    hit_code_ = kHitInnerEnd; return;
  }

  double inner = min(arc_annulus_.inner, arc_annulus_.outer);
  double outer = max(arc_annulus_.inner, arc_annulus_.outer);

  // Test if mouse hit annulus start spoke
  double width = outer-inner;
  geometry::Point2D vertexes[4] = {vertexes_[kInnerStart], vertexes_[kInnerStart],
  vertexes_[kOuterStart], vertexes_[kOuterStart] };
  InflateRect(vertexes, width, 0, 0, kTolerance);
  if(PtInRect(vertexes, pt))
  {
    hit_code_ = kHitStartSpoke; return;
  }

  // Test if mouse hit annulus end spoke
  vertexes[0] = vertexes_[kOuterEnd]; vertexes[1] = vertexes_[kOuterEnd];
  vertexes[2] = vertexes_[kInnerEnd]; vertexes[3] = vertexes_[kInnerEnd];
  InflateRect(vertexes, width, 0, 0, kTolerance);
  if(PtInRect(vertexes, pt))
  {
    hit_code_ = kHitEndSpoke; return;
  }

  geometry::Point2D vec(pt.x() - arc_annulus_.x,  arc_annulus_.y - pt.y());
  double angle = VectorAngle(vec.x(), vec.y());
  if (!AngleInRange(angle, arc_annulus_.start, arc_annulus_.end)) {
    return;
  }

  geometry::Point2D center(arc_annulus_.x, arc_annulus_.y);
  bool bOuterFar = PtInCircle(pt, center, arc_annulus_.outer+kTolerance);
  bool bOuterNer = PtInCircle(pt, center, max(0, arc_annulus_.outer-kTolerance));

  // Test if mouse hit annulus outer border
  if (bOuterFar && !bOuterNer)
  {
    hit_code_ = kHitOuterBorder; return;
  }

  bool bInnerFar = PtInCircle(pt, center, arc_annulus_.inner+kTolerance);
  bool bInnerNer = PtInCircle(pt, center, max(0, arc_annulus_.inner-kTolerance));

  // Test if mouse hit annulus inner border
  if (bInnerFar && !bInnerNer)
  {
    hit_code_ = kHitInnerBorder; return;
  }

  // Test if mouse hit annulus center marker
  if (PtInCircle(pt, center, min(inner, kMarkerSize) + kTolerance))
  {
    hit_code_ = kHitMiddle; return;
  }

  // Test if mouse hit annulus interior
  if(PtInCircle(pt, center, outer-kTolerance) &&
    !PtInCircle(pt, center, inner+kTolerance))
  {
    hit_code_ = kHitMiddle; return;
  }
}

void CArcAnnulus::TrackTo(const geometry::Point2D& pt)
{
  geometry::Point2D vec(pt.x()-arc_annulus_.x,  arc_annulus_.y-pt.y());

  switch(hit_code_)
  {
  case kHitInnerBorder:
    TrackInnerBorder(vec);
    break;

  case kHitOuterBorder:
    TrackOuterBorder(vec);
    break;

  case kHitStartSpoke:
    TrackStartSpoke(vec);
    break;

  case kHitEndSpoke:
    TrackEndSpoke(vec);
    break;

  case kHitInnerStart:
    TrackInnerBorder(vec);
    TrackStartSpoke(vec);
    break;

  case kHitOuterStart:
    TrackOuterBorder(vec);
    TrackStartSpoke(vec);
    break;

  case kHitInnerEnd:
    TrackInnerBorder(vec);
    TrackEndSpoke(vec);
    break;

  case kHitOuterEnd:
    TrackOuterBorder(vec);
    TrackEndSpoke(vec);
    break;

  case kHitMiddle:
    {
      geometry::Point2D delt(pt.x()-anchor_.x(), pt.y()-anchor_.y());
      arc_annulus_.x += delt.x();
      arc_annulus_.y += delt.y();
    }
    break;

  default : break;
  }

  anchor_  = pt;
}
